﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Controllers;
using SageKing.OpenIddict.UI.Identity.Api;
using SageKing.OpenIddict.UI.Identity.Api.Features;
using SageKing.OpenIddict.UI.Infrastructure.Data;

namespace SageKing.Extensions;

public static class ModuleExtensions
{
    public static IModule OpenIddictUIApiServer(this IModule module, Action<OpenIddictUIApiFeature>? configure = default)
    {
        module.Configure<OpenIddictUIApiFeature>(feature =>
        {
            configure?.Invoke(feature);
        });

        module.UseIceMediatR(o => o.MediatRServiceConfiguration += a =>
        {
            a.RegisterServicesFromAssembly(typeof(OpenIddictUIApiFeature).Assembly);
        });


        return module;
    }

    public static OpenIddictBuilder AddUIIdentityApis<TApplicationUser>(
    this OpenIddictBuilder builder,
    Action<OpenIddictUIIdentityApiOptions> uiApiOptions = null
  )
    where TApplicationUser : AppUser, new() => AddUIIdentityApis<TApplicationUser, AppRole, long>(builder, uiApiOptions);

    public static OpenIddictBuilder AddUIIdentityApis<TApplicationUser, TApplicationRole, TKey>(
      this OpenIddictBuilder builder,
      Action<OpenIddictUIIdentityApiOptions> uiApiOptions = null
    )
      where TKey : IEquatable<TKey>
      where TApplicationUser : TenantIdentityUser<TKey>, new()
      where TApplicationRole : TenantIdentityRole<TKey>, new()
    {
        var options = new OpenIddictUIIdentityApiOptions();
        uiApiOptions?.Invoke(options);
        builder.AddRoutePrefix(options.RoutePrefix);

        builder.Services.AddApiServices<TApplicationUser, TApplicationRole, TKey>();

        builder.Services.AddAuthorizationServices(options.Policy);

        return builder;
    }

    /// <summary>
    /// Registers the UserName to UserName UserCreationStrategy.
    /// </summary>
    public static OpenIddictBuilder AddUserNameUserCreationStrategy<TApplicationUser, TKey>(
      this OpenIddictBuilder builder
    )
      where TKey : IEquatable<TKey>
      where TApplicationUser : IdentityUser<TKey>, new()
    {
        builder.Services
          .AddUserNameUserCreationStrategy<TApplicationUser, TKey>();

        return builder;
    }

    internal static IServiceCollection AddApiServices<TApplicationUser, TApplicationRole, TKey>(
      this IServiceCollection services
    )
      where TKey : IEquatable<TKey>
      where TApplicationUser : IdentityUser<TKey>, new()
      where TApplicationRole : IdentityRole<TKey>, new()
    {
        services.AddTransient<IAccountApiService, AccountApiService<TApplicationUser, TKey>>();
        services.AddKeyedTransient<IAccountApiClient, AccountApiClient<TApplicationUser, TKey>>(HttpClientConsts.ServerKey);
        services.AddKeyedTransient<IAccountApiClient, AccountApiHttpClient<TApplicationUser, TKey>>(HttpClientConsts.ClientKey);
        services.AddTransient<IRoleApiService, RoleApiService<TApplicationRole, TKey>>();
        services.AddTransient<IClaimTypeApiService, ClaimTypeApiService>();

        return services;
    }

    internal static IServiceCollection AddAuthorizationServices(
      this IServiceCollection services,
      Action<AuthorizationPolicyBuilder> policy
    )
    {
        services.AddAuthorizationBuilder()
            .AddPolicy(Policies.OpenIddictUiIdentityApiPolicy, policy
    );

        return services;
    }

    internal static OpenIddictBuilder AddRoutePrefix(
      this OpenIddictBuilder builder,
      string routePrefix
    )
    {
        builder.Services.AddControllers(options =>
        {
            options.UseOpenIddictUIRoutePrefix(
              routePrefix,
              [
                typeof(AccountController),
          typeof(ClaimTypeController),
          typeof(RoleController)
              ]
            );
        });

        return builder;
    }
    internal static IServiceCollection AddRoutePrefix(
  this IServiceCollection services,
  string routePrefix
)
    {
        services.AddControllers(options =>
        {
            options.UseOpenIddictUIRoutePrefix(
              routePrefix,
              [
                typeof(AccountController),
          typeof(ClaimTypeController),
          typeof(RoleController)
              ]
            );
        });

        return services;
    }
}
